package mka.puzzle.shape;

import java.awt.Graphics;
import java.util.ArrayList;

/***
 * Line
 * 
 * The General equation for a Line: ax + by + c= 0 Y = AX + B
 * 
 * @version 20131224
 * 
 * @author NguyenBB
 */
public class Line extends Conic {
	/**
	 * Slope of Line.
	 */
	private double k;

	/**
	 * Axis Y intercept.
	 */
	private double b;

	/**
	 * Point begin line.
	 */
	private Point pointBegin;

	/**
	 * Point end line.
	 */
	private Point pointEnd;

	/**
	 * Constructor.
	 */
	public Line() {
		this.k = 0;
		this.b = 0;
		this.pointBegin = new Point(0, 0);
		this.pointEnd = new Point(0, 0);
	}

	/**
	 * Constructor(Line(Point pointBegin, Point pointB)).
	 */
	public Line(Point pointA, Point pointB) {
		// if x position of pointA low than pointB, swap pointA and pointB.
		if (pointA.getX() > pointB.getX()) {
			this.pointBegin = new Point(pointB.getX(), pointB.getY());
			this.pointEnd = new Point(pointA.getX(), pointA.getY());
		} else {
			this.pointBegin = new Point(pointA.getX(), pointA.getY());
			this.pointEnd = new Point(pointB.getX(), pointB.getY());
		}

		// Slop of line is infinity
		if (pointBegin.getX() == pointEnd.getX()) {
			this.b = 0;
			this.k = 0;
		} else {
			// Calculate k and b.
			this.k = (pointEnd.getY() - pointBegin.getY())
					/ (pointEnd.getX() - pointBegin.getX());
			this.b = pointBegin.getY() - this.k * pointBegin.getX();
		}
	}

	public Point getPointBegin() {
		return pointBegin;
	}

	public void setPointBegin(Point pointBegin) {
		this.pointBegin = pointBegin;
	}

	public Point getPointEnd() {
		return pointEnd;
	}

	public void setPointEnd(Point pointEnd) {
		this.pointEnd = pointEnd;
	}

	public String getName() {
		return "Line";
	}

	/**
	 * containsLine.
	 * 
	 * @param p
	 *            Point
	 * @return boolean
	 */
	public boolean containsLine(Point p) {
		if (p.getY() == Math.round(p.getX() * this.k + this.b))
			return true;
		return false;
	}

	/**
	 * estimateY.
	 * 
	 * @param x
	 *            double
	 * @return double
	 */
	public double estimateY(double x) {
		double yTemp = this.k * x + this.b;
		return yTemp;
	}

	/**
	 * allPointLine. Get all point on Line by equation of line.
	 * 
	 * @return ArrayList<Point>
	 */
	public ArrayList<Point> allPointLine() {
		ArrayList<Point> pointList = new ArrayList<Point>();
		double minY = this.pointBegin.getY() < this.pointEnd.getY() ? this.pointBegin
				.getY() : this.pointEnd.getY();
		double maxY = this.pointBegin.getY() > this.pointEnd.getY() ? this.pointBegin
				.getY() : this.pointEnd.getY();

		if (this.pointBegin.getX() == this.pointEnd.getX()) {
			for (double y = minY; y < maxY; y = y + 0.01)
				pointList.add(new Point(this.pointBegin.getX(), y));
			return pointList;
		}

		double y = 0;
		for (double x = this.pointBegin.getX(); x <= this.pointEnd.getX(); x = x + 0.01) {
			y = estimateY(x);
			pointList.add(new Point(x, y));
		}
		return pointList;
	}

	/**
	 * allPointBresenham. Get all point on Line by algorithm Bresenham.
	 * 
	 * @return ArrayList<Point>
	 */
	public ArrayList<Point> allPointBresenham() {
		ArrayList<Point> pointList = new ArrayList<Point>();
		long dx, dy, p, c1, c2;
		dx = Math.abs((long) this.pointEnd.getX()
				- (long) this.pointBegin.getX());
		dy = Math.abs((long) this.pointEnd.getY()
				- (long) this.pointBegin.getY());
		p = 2 * dy - dx;
		c1 = 2 * dy;
		c2 = 2 * (dy - dx);
		double x = this.pointBegin.getX();
		double y = this.pointBegin.getY();

		int x_unit = 1, y_unit = 1;

		pointList.add(new Point(x, y));
		while (x != this.pointEnd.getX()) {

			if (p < 0)
				p += c1;
			else {
				p += c2;
				y += y_unit;
			}
			x += x_unit;
			pointList.add(new Point(x, y));
		}
		return pointList;
	}

	/***************************************************************
	 * Override from superclass.
	 ***************************************************************/

	@Override
	public boolean containBound(Point p) {
		// TODO Auto-generated method stub
		return containsLine(p);
	}

	@Override
	public boolean containShape(Point p) {
		// TODO Auto-generated method stub
		return containsLine(p);
	}

	@Override
	public ArrayList<Point> allPointBound() {
		// TODO Auto-generated method stub
		return allPointLine();
	}

	@Override
	public ArrayList<Point> allPointShape() {
		// TODO Auto-generated method stub
		return allPointLine();
	}

	@Override
	public double getPerimeter() {
		// TODO Auto-generated method stub
		return Math.pow(this.pointBegin.getX() - this.pointEnd.getX(), 2)
				+ Math.pow(this.pointBegin.getY() - this.pointEnd.getY(), 2);
	}

	@Override
	public double getArea() {
		// TODO Auto-generated method stub
		return 0;
	}

	/***************************************************************
	 * Draw and Paint Object.
	 ***************************************************************/
	@Override
	public void draw(Graphics g) {
		g.drawLine((int) this.getPointBegin().getX(), (int) this
				.getPointBegin().getY(), (int) this.getPointEnd().getX(),
				(int) this.getPointEnd().getY());
	}
}
